// BSD License (http://www.galagosearch.org/license)

package org.galagosearch.core.tools;

import org.galagosearch.core.retrieval.*;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.galagosearch.core.parse.VersionedDocument;
import org.galagosearch.core.retrieval.query.Node;
import org.galagosearch.core.retrieval.query.SimpleQuery;
import org.galagosearch.core.retrieval.query.StructuredQuery;
import org.galagosearch.core.store.DocumentStore;
import org.galagosearch.core.store.SnippetGenerator;
import org.galagosearch.tupleflow.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Element;


/**
 *
 * @author trevor
 */
public class VersionedSearch {
    SnippetGenerator generator;
    DocumentStore store;
    Retrieval retrieval;
    boolean isTC=false;
    public VersionedSearch(Retrieval retrieval, DocumentStore store) {
        this.store = store;
        this.retrieval = retrieval;
        generator = new SnippetGenerator();
    }
    public VersionedSearch(Retrieval retrieval, DocumentStore store, boolean isTC) {
        this.store = store;
        this.retrieval = retrieval;
        generator = new SnippetGenerator();
        this.isTC=isTC;
    }

    public void close() throws IOException {
        store.close();
        retrieval.close();
    }

    public static class SearchResult {
        public Node query;
        public Node transformedQuery;
        public List<SearchResultItem> items;
    }

    public static class SearchResultItem {
        public int rank;
        public String identifier;
        public String displayTitle;
        public String url;
        public Map<String, String> metadata;
        public String summary;
    }

    public String getSummary(VersionedDocument document, Set<String> query) throws IOException {
        if (document.metadata.containsKey("description")) {
            String description = document.metadata.get("description");

            if (description.length() > 10) {
                return generator.highlight(description, query);
            }
        }

        return generator.getSnippet(document.text, query);
    }

    public static Node parseQuery(String query, Parameters parameters) {
        String queryType = parameters.get("queryType", "complex");

        if (queryType.equals("simple")) {
            return SimpleQuery.parseTree(query);
        }

        return StructuredQuery.parse(query);
    }

    public VersionedDocument getDocument(String identifier) throws IOException {
        return VersionedDocument.toVersionedDocument(store.get(identifier));		
    }

    public SearchResult runQuery(String query, String queryDate, int startAt, int count, boolean summarize) throws Exception {
        Node tree = parseQuery(query, new Parameters());
        Node transformed = retrieval.transformQuery(tree);
        ScoredDocument[] results;
        retrieval.ipTime = queryDate;
        if(isTC) results= retrieval.runQueryTC(transformed, startAt + count);
        else results= retrieval.runQuery(transformed, startAt + count);
        SearchResult result = new SearchResult();
        Set<String> queryTerms = StructuredQuery.findQueryTerms(tree);
        result.query = tree;
        result.transformedQuery = transformed;
        result.items = new ArrayList();

        for (int i = startAt; i < Math.min(startAt + count, results.length); i++) {
            String identifier = retrieval.getDocumentName(results[i].document);
            int tempPos=identifier.lastIndexOf('+');
            int revID=Integer.parseInt(identifier.substring(tempPos+1));
            identifier=identifier.substring(0, tempPos);
            tempPos=identifier.lastIndexOf('+');
            String timeEnd=identifier.substring(tempPos+1);
            identifier=identifier.substring(0, tempPos);
            tempPos=identifier.lastIndexOf('+');
            String timeBegin=identifier.substring(tempPos+1);
            identifier=identifier.substring(0,tempPos);
            
            if(isTC || checkDocumentValidity(queryDate,timeBegin,timeEnd))
            {
	            VersionedDocument document = getDocument(identifier,revID);
	            SearchResultItem item = new SearchResultItem();
	
	            item.rank = i + 1;
	            item.identifier = identifier;
	            item.displayTitle = identifier;
	
	            if (document.metadata.containsKey("title")) {
	                item.displayTitle = document.metadata.get("title");
	            }
	
	            if (item.displayTitle != null) {
	                item.displayTitle = generator.highlight(item.displayTitle, queryTerms);
	            }
	
	            if (document.metadata.containsKey("url")) {
	                item.url = document.metadata.get("url");
	            }
	
	            if (summarize) {
	                item.summary = getSummary(document, queryTerms);
	            }
	
	            item.metadata = document.metadata;
	            if(isTC==true){
	            	//String strTimeBegin = results[i].timeB.toString();
	            	//String strTimeBegin = results[i].timeB.toString();
	            	//item.metadata.put("timeBegin",intTimeToStringTime(results[i].timeB));
	            	//item.metadata.put("timeEnd",intTimeToStringTime(results[i].timeE));
	            	item.metadata.put("timeBegin",Integer.toString(results[i].timeB));
	            	item.metadata.put("timeEnd",Integer.toString(results[i].timeE));
	            }
	            result.items.add(item);
            }
        }
    /*    if(result.items.size()<count)
        {
        	SearchResult additionResult  =runQuery(query, startAt+count, 1, summarize);
        	if(additionResult.items.size()>0) result.items.add(additionResult.items.get(0));
        }*/
        return result;
    }
    public String intTimeToStringTime(int timeStamp){
    	String strTimeStamp = Integer.toString(timeStamp);
    	//System.out.println("String: "+strTimeStamp+"\nLength:"+strTimeStamp.length());
    	if(timeStamp == -1){
    		return strTimeStamp;
    	}
    	if(strTimeStamp.length() == 9){
    		return  strTimeStamp.substring(0,1)+"-"+strTimeStamp.substring(1,3)+"-"+strTimeStamp.substring(3,5)+":"+strTimeStamp.substring(5,7)+":"+strTimeStamp.substring(7,9);
    	}
    	return  strTimeStamp.substring(0,2)+"-"+strTimeStamp.substring(2,4)+"-"+strTimeStamp.substring(4,6)+":"+strTimeStamp.substring(6,8)+":"+strTimeStamp.substring(8,10);
    }
    
    public VersionedDocument getDocument(String fileName,int revNumber)
    {
    	
    	try{
    		Document docDOM = getDOMParserObject(fileName);
            int noOfRevisions = getNumberOfRevisions(docDOM,"revision");
            NodeList revList = getNodeList(docDOM,"revision");
            String fullFileText = getFullDataFile(fileName);
    		
            VersionedDocument vd=new VersionedDocument();
            vd.identifier=fileName;
            vd.metadata.put("title", getTitle(fullFileText));
            vd.metadata.put("timeBegin", getTimestamp(revList,"timestamp",revNumber));
            if(revNumber < noOfRevisions-1){
            	vd.metadata.put("timeEnd", getTimestamp(revList,"timestamp",revNumber+1));
            }
            else{
            	vd.metadata.put("timeEnd","-1");
            }
            vd.text=getDocumentText(fileName, revNumber,fullFileText);
            
            
            return vd;
    	}catch(Exception e)
    	{
    		return null;
    	}
    }
    
    public String getTimestamp(NodeList revList,String elementName,int revNumber) throws Exception{
    	org.w3c.dom.Node nNode = revList.item(revNumber);
    	Element eElement = (Element) nNode;
    	NodeList nlList = eElement.getElementsByTagName(elementName).item(0).getChildNodes();
    	org.w3c.dom.Node nValue = (org.w3c.dom.Node) nlList.item(0);
        //System.out.println("TS: "+nValue.getNodeValue());
        return nValue.getNodeValue();
    }
    
    public String getFullDataFile(String fName) throws Exception{
    	FileInputStream fstream = new FileInputStream(fName);
 		DataInputStream in = new DataInputStream(fstream);
 		BufferedReader br = new BufferedReader(new InputStreamReader(in));

 		StringBuilder builder = new StringBuilder();
        String line;
 		
         while ((line = br.readLine()) != null) {
             builder.append(line);
             builder.append("\n");
         }
         return builder.toString();
    }
    
    public String getTitle(String text) {
    	
    	
        int start = text.indexOf("<title>");
        if (start < 0) return "";
        int end = text.indexOf("</title>");
        if (end < 0) return "";
        return new String(text.substring(start + "<title>".length(), end));
    }
    
    public int getNumberOfRevisions(Document documentDOM,String elementName) throws Exception{
    	NodeList nList = documentDOM.getElementsByTagName(elementName);
    	return nList.getLength();
    }
    
    public NodeList getNodeList(Document documentDOM,String elementName) throws Exception{
    	return documentDOM.getElementsByTagName(elementName);
    }
    public Document getDOMParserObject(String xmlFName) throws Exception{
    	File fXmlFile = new File(xmlFName);
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
		Document doc = dBuilder.parse(fXmlFile);
		doc.getDocumentElement().normalize();
		return doc;
    }
    
    public String getDocumentText(String fileName,int revNumber,String fullFile) throws Exception{
 		String dataContent = ""; 	
 		int currRevNumber = 0;
 		
 		if(revNumber == 0 && 1==0){
 			return null;
 		}
 		else{
 			while(currRevNumber<revNumber){
 				int start = fullFile.indexOf("<revision>");
 				if (start < 0) return "";
 				int end = fullFile.indexOf("</revision>");
 				if (end < 0) return "";
 				fullFile = fullFile.substring(end + "</revision>".length(), fullFile.length());
 				currRevNumber = currRevNumber + 1;
 			}
 			int start = fullFile.indexOf("<revision>");
 			if (start < 0) return "";
 			int end = fullFile.indexOf("</revision>");
 			if (end < 0) return "";
 			dataContent = fullFile.substring(start + "<revision>".length(), end);
 		}
 		return dataContent;
 	}
    public boolean checkDocumentValidity(String inputTime, String timeBegin, String timeEnd){

		boolean isValid = false;
		
		//int inpTime = stringTimetoIntTime(inputTime);
		int inpTime = stringInputTImetoIntInputTime(inputTime);
		int timeB = stringTimetoIntTime(timeBegin);
		if(timeEnd.trim().compareTo("-1")==0){
			//int timeE = -1;
			if(timeB<=inpTime)
				isValid = true;
		}
		else{
			int timeE = stringTimetoIntTime(timeEnd);
			if(timeB<=inpTime && timeE>=inpTime)
				isValid = true;
		}	
		return isValid;
	}
	private int stringInputTImetoIntInputTime(String inputTime) {
		// TODO Auto-generated method stub
		StringTokenizer str = new StringTokenizer(inputTime,"-");
		
		String s1=(String) str.nextElement();
		String s2=(String) str.nextElement();
		String s3=(String) str.nextElement();
		
		if(s2.length()==1)
			s2 = "0"+s2;
		
		String outputString = s3.substring(2, 4) + s2 + s1 + "0000";
	//	System.out.println("year = "+s3.substring(2, 4)+"month = "+s2+"Date: "+s1+"Output: "+outputString);
		return Integer.parseInt(outputString);
	}

	public int stringTimetoIntTime(String timeStamp){

//		System.out.println("timeStamp:"+timeStamp);
		String substr=timeStamp.substring(2,4);
		String s1=timeStamp.substring(5,7);
		String s2=timeStamp.substring(8,10);
		String s3=timeStamp.substring(11,13);
		String s4=timeStamp.substring(14,16);
		String str= substr + s1  + s2 + s3 + s4;
		int i=Integer.parseInt(str);
		return i;
	}
}
